#pragma once

#include <cstdlib>

namespace snow
{

class Alloc
{
public:
    static void *allocate(size_t bytes);
    static void deallocate(void *p, size_t bytes);
    static void *reallocate(void *p, size_t old_sz, size_t new_sz);

private:
    // 将bytes上调至8的整数倍
    static size_t ROUND_UP(size_t bytes)
    {
        return (bytes + _ALIGN - 1) & ~(_ALIGN - 1);
    }
    // 找到对应freelist指针数组的索引
    static size_t FREELIST_INDEX(size_t bytes)
    {
        return (bytes + _ALIGN - 1) / _ALIGN - 1;
    }
    // 填充freelist，优先从内存池取，一个返回给用户，剩余的挂到freelist上
    static void *refill(size_t bytes);
    // chunk空间 bytes*nobjs，优先从内存池取，不够从堆空间chunk
    static char *chunk_alloc(size_t bytes, size_t &nobjs);

private:
    enum { _ALIGN = 8 };                       // 小型区块上调边界 8
    enum { _MAXBYTES = 128 };                  // 小型区块上限 128
    enum { _NFREELISTS = _MAXBYTES / _ALIGN }; // 自由空闲链表个数 16个
    enum { _NOBJS = 20 };                      // 一次申请20个区块

    // freelist节点构造，是个union不会有额外的空间负担
    union _obj {
        union _obj *next;
        char client[1];
    };
    static _obj *_free_list[_NFREELISTS];

    // 内存池指针，只在chunk_alloc中变化
    static char *_start_free;
    static char *_end_free;
    static size_t _heap_size; // 记录从堆中申请多少内存，只增不减
};

} // end of namespace snow